/** 1. 定义类 */

// 定义食物
class Food {
    // 定义一个属性表示食物对应的元素
    element: HTMLElement;

    constructor() {
        // 获取页面中food元素并赋值给element
        this.element = document.getElementById('food')!;
    }

    // 获取食物x轴坐标
    X() {
        return this.element.offsetLeft
    }
    // 获取食物y轴坐标
    Y() {
        return this.element.offsetTop
    }

    // 修改食物位置
    change() {
        // 生成一个随机位置
        // left:  0 - 290
        // top: 0 - 290
        // 蛇移动一格大小为10

        // 0 - 29
        let left = Math.round(Math.random() * 29) * 10;
        let top = Math.round(Math.random() * 29) * 10;

        this.element.style.left = left + 'px';
        this.element.style.top = top + 'px';
    }
}


// 定义记分牌
class ScorePanel {
    score = 0;
    level = 1;

    // 设置参数限制等级
    maxLevel =  10;
    // 设置变量表示升级需要的分数
    upScore = 10;

    scoreEle: HTMLElement;
    levelEle: HTMLElement;

    constructor(maxLevel: number = 10, upScore: number = 10) {
        this.scoreEle = document.getElementById('score')!;
        this.levelEle = document.getElementById('level')!;
        this.maxLevel = maxLevel;
        this.upScore = upScore;
    }

    // 加分方法
    addScore() {
        this.scoreEle.innerHTML = ++this.score + '';
        // 判断分数是多少
        if(this.score % this.upScore === 0) {
            this.upLevel();
        }
    }

    // 等级提升方法
    upLevel() {
        if(this.level < this.maxLevel) {
            this.levelEle.innerHTML = ++this.level + '';
        }
    }
}

// const scorePanel = new ScorePanel();
// const time = 10;
// let timer = setInterval(() => {
//     let cd = scorePanel.score;
//     if(cd <= time) {
//         scorePanel.addScore();
//     }else{
//         clearInterval(timer)
//     }
// },1000);

// 定义蛇
class Snake {
    // 表示蛇头元素
    snakeHead: HTMLElement;
    // 表示蛇身 包括蛇头
    snakeBody: HTMLCollection;
    // 表示蛇整体(容器)
    element: HTMLElement;

    constructor() {
        this.element = document.getElementById('snake')!;
        this.snakeHead = document.querySelector('#snake > div') as HTMLElement;
        this.snakeBody = this.element.getElementsByTagName('div');
    }

    // 获取蛇坐标(即蛇头的坐标)
    X() {
        return this.snakeHead.offsetLeft
    }
    Y() {
        return this.snakeHead.offsetTop
    }

    // 设置蛇头的坐标
    setX(left: number) {
        // 如果新值和旧值相同，直接返回不做修改
        if(this.X() === left) { return }

        // X值合法范围  0 - 290
        if(left < 0 || left > 290) {
            // 进入判断说明蛇撞墙了
            throw new Error('小蛇蛇撞墙了！')
        }

        // 水平方向发生掉头
        if(this.snakeBody[1] && (this.snakeBody[1] as HTMLElement).offsetLeft === left) {
            // console.log('水平方向发生掉头');
            // 如果发生掉头，令其不掉头，继续当前方向移动
            if(left > this.X()) {
                // 如果新值大于旧值，则说明蛇再向右移动，此时发生掉头，应该使蛇继续向左走
                left = this.X() - 10;
            }else{
                left = this.X() + 10;
            }
        }

        // 身体移动
        this.moveBody();
        // 蛇头移动
        this.snakeHead.style.left = left + 'px';
        // 检测蛇头和蛇身是否相撞
        this.checkHeadBody();

    }
    setY(top: number) {
        // 如果新值和旧值相同，直接返回不做修改
        if(this.Y() === top) { return }

        // Y值合法范围  0 - 290
        if(top < 0 || top > 290) {
            // 进入判断说明蛇撞墙了
            throw new Error('小蛇蛇撞墙了！')
        }

        // 垂直方向发生掉头
        if(this.snakeBody[1] && (this.snakeBody[1] as HTMLElement).offsetTop === top) {
            // console.log('水平方向发生掉头');
            // 如果发生掉头，令其不掉头，继续当前方向移动
            if(top > this.Y()) {
                // 如果新值大于旧值，则说明蛇再向右移动，此时发生掉头，应该使蛇继续向左走
                top = this.Y() - 10;
            }else{
                top = this.Y() + 10;
            }
        }

        // 身体移动
        this.moveBody();
        // 蛇头移动
        this.snakeHead.style.top = top + 'px';
        // 检测蛇头和蛇身是否相撞
        this.checkHeadBody();
    }

    // 蛇身增长的方法
    addSnakeBody() {
        // 向element中添加div
        this.element.insertAdjacentHTML('beforeend', '<div></div>')
    }

    // 蛇身体移动
    moveBody() {
        // 将蛇后面的身体设置为前面身体的位置
        /*
        *       第四节 = 第三节位置
        *       第三节 = 第二节位置
        *       第二节 = 第一节位置
        *       第一节 = 新位置
        * */
        for(let i=this.snakeBody.length-1; i>0; i--) {
            // 获取前边身体的位置
            let x = (this.snakeBody[i-1] as HTMLElement).offsetLeft;
            let y = (this.snakeBody[i-1] as HTMLElement).offsetTop;

            // 将值设置上去
            (this.snakeBody[i] as HTMLElement).style.left = x + 'px';
            (this.snakeBody[i] as HTMLElement).style.top = y + 'px';
        }
    }

    // 检查蛇头和蛇身是否相撞
    checkHeadBody() {
        // 获取蛇身的所有位置，检查其与蛇头是否发生重叠
        for(let i=1; i<this.snakeBody.length; i++) {
            let bd = this.snakeBody[i] as HTMLElement;
            if(this.X() === bd.offsetLeft && this.Y() === bd.offsetTop) {
                // 蛇头撞到蛇身，抛出错误，游戏结束
                throw new Error('小蛇蛇撞到自己了');
            }
        }
    }

}

// 定义游戏控制器
class GameControl {
    // 定义三个属性
    // 蛇
    snake: Snake;
    // 食物
    food: Food;
    // 记分牌
    scorePanel: ScorePanel;
    // 定义蛇移动方向(按键方向)
    direction: string = '';
    // 游戏是否结束
    isLive = true;

    constructor() {
        this.snake = new Snake();
        this.food = new Food();
        this.scorePanel = new ScorePanel(10,2);

        this.init();
    }

    // 游戏初始化，游戏开始
    init() {
        // 绑定键盘按下的事件
        document.addEventListener('keydown', this.keyDownHandler.bind(this));

        // 让蛇动起来
        this.snakeRun();
    }

    /*
    *
    * ArrowUp 上        Up
    * ArrowDown 下        Down
    * ArrowLeft 左        Left
    * ArrowRight 右        Right
    *
    * */
    // 键盘按下的响应函数
    keyDownHandler(event: KeyboardEvent) {
        // 检查event.key的值是否合法(用户是否按了正确的按键)

        // 修改direction(蛇移动方向)
        this.direction = event.key;
    }

    // 创建蛇移动的方法
    snakeRun() {
        /*
           根据direction控制蛇移动方向
           向上  top--
           向下  top++
           向左  left--
           向右  left++
        * */
        // 获取蛇现在的坐标
        let x = this.snake.X();
        let y = this.snake.Y();

        // 根据案件方向修改x,y值
        switch (this.direction) {
            case "ArrowUp":
            case "Up":
                // 向上 top--
                y -= 10;
                break;
            case "ArrowDown":
            case "Down":
                // 向下 top++
                y += 10;
                break;
            case "ArrowLeft":
            case "Left":
                // 向左 left--
                x -= 10;
                break;
            case "ArrowRight":
            case "Right":
                // 向右 left++
                x += 10;
                break;
        }

        // 检查蛇是否吃到了食物
        this.checkEat(x, y);

        // 修改蛇的X 和 Y 值
        try{
            this.snake.setX(x);
            this.snake.setY(y);
        }catch (e) {
            alert(e + 'GAME OVER!');
            this.isLive = false;
        }

        //开启一个定时调用
        // isLive为true 表示游戏进行当中，开启定时器蛇继续移动；相反关闭定时器，蛇不能移动
        this.isLive && setTimeout(this.snakeRun.bind(this), 300 - (this.scorePanel.level-1)*30);
    }

    // 定义一个方法，检测蛇吃到了食物
    checkEat(X:number, Y: number) {
        if(X === this.food.X() && Y === this.food.Y()) {
            // 食物的位置要重置
            this.food.change();
            // 蛇增加一节
            this.snake.addSnakeBody();
            // 分数增加一分
            this.scorePanel.addScore();
        }
    }
}

const gameControl = new GameControl();
// setInterval(() => {
//     console.log(gameControl.direction);
// }, 1000);
